#include "colorhistogram.h"

using namespace controllers;

ColorHistogram::ColorHistogram() {
    // Prepare arguments for a color histogram
    _histSize[0] = _histSize[1] = _histSize[2] = 256;

    // BRG range
    _hranges[0] = 0.0;
    _hranges[1] = 255.0;

    // all channels have the same range
    _ranges[0] = _hranges;
    _ranges[1] = _hranges;
    _ranges[2] = _hranges;

    // the three channels
    _channels[0] = 0;
    _channels[1] = 1;
    _channels[2] = 2;
}

ColorHistogram::~ColorHistogram()
{
}

/**
 * @brief ColorHistogram::applyLookUp
 * @param image
 * @param lookup
 * @return
 */
Mat ColorHistogram::applyLookUp(const Mat& image, // input image
                                const Mat& lookup)// 1x256 uchar matrix
{
    // the output image
    Mat result;

    // apply lookup table
    LUT(image, lookup, result);

    return result;
}

/**
 * @brief ColorHistogram::equalize
 * @param image
 * @return
 */
Mat ColorHistogram::equalize(const Mat &image)
{
    Mat result;
    equalizeHist(image, result);

    return result;
}

/**
 * @brief ColorHistogram::stretch
 * @param image
 * @param minValue
 * @return
 */
Mat ColorHistogram::stretch(const Mat &image, int minValue) {
    // Compute histogram first
    MatND hist = getHistogram(image);

    // find left extremity of the histogram
    int imin = 0;
    for( ; imin < _histSize[0]; imin++ )
    {
        std::cout << hist.at<float>(imin) << std::endl;

        if (hist.at<float>(imin) > minValue)
            break;
    }

    // find right extremity of the histogram
    int imax = _histSize[0] - 1;
    for( ; imax >= 0; imax-- )
    {
        if (hist.at<float>(imax) > minValue)
            break;
    }

    // Create lookup table
    int dim(256);
    Mat lookup(1, // 1 dimension
            &dim, // 256 entries
            CV_8U // uchar
            );

    // Build lookup table
    for (int i = 0; i < 256; i++)
    {
        // stretch between imin and imax
        if (i < imin)
            lookup.at<uchar>(i) = 0;
        else if (i > imax)
            lookup.at<uchar>(i) = 255;
        else // linear mapping
            lookup.at<uchar>(i) = static_cast<uchar>(255.0 * (i - imin) / (imax - imin) + 0.5);
    }

    // Apply lookup table
    Mat result;
    result= applyLookUp(image,lookup);

    return result;
}

/**
 * @brief colorReduce
 * @param image
 * @param div
 */
void ColorHistogram::colorReduce(cv::Mat &image, int div)
{
    // number of lines
    int nl= image.rows;

    // total number of elements per line
    int nc= image.cols * image.channels();

    for (int j=0; j<nl; j++) {
        // get the address of row j
        uchar* data= image.ptr<uchar>(j);
        for (int i=0; i<nc; i++) {
            // process each pixel ---------------------
            data[i]= data[i] / (div * div) + (div / 2);
            // end of pixel processing ----------------
        } // end of line
    }
}

/**
 * @brief ColorHistogram::getHistogram
 * @param image
 * @return
 */
MatND ColorHistogram::getHistogram(const Mat &image) {
    MatND hist;

    // Compute histogram
    calcHist(&image,
            1, // histogram of 1 image only
            _channels, // the channel used
            Mat(), // no mask is used
            hist, // the resulting histogram
            1, // it is a 1D histogram
            _histSize, // number of bins
            _ranges // pixel value range
            );

    return hist;
}

/**
 * @brief ColorHistogram::getSparseHistogram
 * @param image
 * @return
 */
SparseMat ColorHistogram::getSparseHistogram(const Mat &image) {
    SparseMat hist(3, _histSize, CV_32F);

    // Compute histogram
    calcHist(&image,
            1, // histogram of 1 image only
            _channels, // the channel used
            Mat(), // no mask is used
            hist, // the resulting histogram
            3, // it is a 3D histogram
            _histSize, // number of bins
            _ranges // pixel value range
            );

    return hist;
}

/**
 * @brief ColorHistogram::getHueHistogram
 *        Computes the 1D Hue histogram with a mask.
 *        BGR source image is converted to HSV
 *        Pixels with low saturation are ignored
 *
 * @param image
 * @param minSaturation
 * @return
 */
MatND ColorHistogram::getHueHistogram(const Mat &image, int minSaturation)
{
    MatND hist;

    // Convert to HSV color space
    Mat hsv;
    cvtColor(image, hsv, CV_BGR2HSV);

    // Mask to be used (or not)
    Mat mask;
    if (minSaturation > 0)
    {
        // Spliting the 3 channels into 3 images
        std::vector<Mat> v;
        split(hsv,v);

        // Mask out the low saturated pixels
        threshold(v[1], mask, minSaturation, 255, THRESH_BINARY);
    }

    // Prepare arguments for a 1D hue histogram
    _hranges[0] = 0.0;
    _hranges[1] = 180.0;

    // the hue channel
    _channels[0] = 0;

    // Compute histogram
    calcHist(&hsv,
             1, // histogram of 1 image only
             _channels, // the channel used
             mask, // binary mask
             hist, // the resulting histogram
             1, // it is a 1D histogram
             _histSize, // number of bins
             _ranges // pixel value range
             );

    return hist;
}

// Retourne l'histogramme en Hue de la zone de tracking dans l'image
vector<int> ColorHistogram::getHistogramVector(int numBins)
{
    vector<int> histogramVector(numBins);

    /*for (int i = 0; i < numBins; i++) {
        histogramVector[i] = cvRound(cvQueryHistValue_1D(this, i));
    }*/

    return histogramVector;
}
